package com.yzh.luckydraw.domain.strategy.service.draw;

import com.yzh.luckydraw.common.Constants;
import com.yzh.luckydraw.domain.strategy.model.vo.*;
import com.yzh.luckydraw.domain.strategy.model.aggregates.StrategyRich;
import com.yzh.luckydraw.domain.strategy.model.req.DrawReq;
import com.yzh.luckydraw.domain.strategy.model.res.DrawResult;
import com.yzh.luckydraw.domain.strategy.service.algorithm.IDrawAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;


public abstract class AbstractDrawBase extends DrawStrategySupport implements IDrawExec {
    private Logger logger = LoggerFactory.getLogger(AbstractDrawBase.class);

    @Override
    public DrawResult doDraw(DrawReq req) {
        //1.获取抽奖策略
        StrategyRich strategyRich = super.queryStrategyRich(req.getStrategyId());
        StrategyBriefVO strategy = strategyRich.getStrategy();

        //2.校验并初始化数据
        this.checkAndInitRateDate(strategy.getStrategyId(), strategy.getStrategyMode(), strategyRich.getStrategyDetailList());

        //3.获取不再抽奖范围内的列表，包括：库存为空、风控策略、临时调整
        List<String> excludeAwardIds = this.queryExcludeAwardIds(req.getStrategyId());
        //4.执行抽奖算法
        String awardId = drawAlgorithm(req.getStrategyId(), drawAlgorithmGroup.get(strategy.getStrategyMode()), excludeAwardIds);
        //5.包装中奖结果
        return this.buildDrawResult(req.getuId(), req.getStrategyId(), awardId, strategy);
    }

    /**
     * 校验抽奖策略是否已经初始化
     *
     * @param strategyId
     * @param strategyMode
     * @param strategyDetailList
     */
    private void checkAndInitRateDate(Long strategyId, Integer strategyMode, List<StrategyDetailBriefVO> strategyDetailList) {
        //根据抽奖策略模式，获取对应的抽奖服务
        IDrawAlgorithm drawAlgorithm = drawAlgorithmGroup.get(strategyMode);
        //已经初始化过则不需要重复初始化
        if (drawAlgorithm.isExist(strategyId)) {
            return;
        }
        //解析并散列中奖概率数据到散列表
        List<AwardRateInfo> awardRateInfoList = new ArrayList<>(strategyDetailList.size());
        for (StrategyDetailBriefVO strategyDetail : strategyDetailList) {
            awardRateInfoList.add(new AwardRateInfo(strategyDetail.getAwardId(), strategyDetail.getAwardRate()));
        }

        drawAlgorithm.initRateTuple(strategyId, strategyMode, awardRateInfoList);
    }

    /**
     * 获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等。根据业务不同，有不同的具体实现，所以放在子类中实现
     *
     * @param strategyId 策略ID
     * @return 排除的奖品集合
     */
    protected abstract List<String> queryExcludeAwardIds(Long strategyId);

    /**
     * 执行抽奖算法
     *
     * @param strategyId      策略ID
     * @param drawAlgorithm   算法模型
     * @param excludeAwardIds 排除的奖品集合
     * @return
     */

    protected abstract String drawAlgorithm(Long strategyId, IDrawAlgorithm drawAlgorithm, List<String> excludeAwardIds);

    /**
     * 包装抽奖结果
     *
     * @param uId        用户ID
     * @param strategyId 策略ID
     * @param awardId    奖品ID，并发抽奖情况下，库存临界值1 -> 0，会有用户中奖结果为null
     * @return 中奖结果
     */
    private DrawResult buildDrawResult(String uId, Long strategyId, String awardId, StrategyBriefVO strategyBriefVO) {
        if (awardId == null) {
            logger.info("执行策略抽奖完成【未中奖】，用户：{} 策略ID：{}", uId, strategyId);
            return new DrawResult(uId, strategyId, Constants.DrawState.FAIL.getCode());
        }

        AwardBriefVO award = super.queryAwardInfoByAwardId(awardId);
        DrawAwardVO drawAwardVO = new DrawAwardVO(uId, award.getAwardId(), award.getAwardType(), award.getAwardName(), award.getAwardContent());
        drawAwardVO.setStrategyMode(strategyBriefVO.getStrategyMode());
        drawAwardVO.setGrantType(strategyBriefVO.getGrantType());
        drawAwardVO.setGrantDate(strategyBriefVO.getGrantDate());
        logger.info("执行策略抽奖完成【已中奖】，用户：{} 策略ID：{} 奖品ID：{} 奖品名称：{}", uId, strategyId, awardId, award.getAwardName());

        return new DrawResult(uId, strategyId, Constants.DrawState.SUCCESS.getCode(), drawAwardVO);
    }
}
